﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using WinFromMediaPlayerDemo.Interface;
using WinFromMediaPlayerDemo.Model;
using WinFromMediaPlayerDemo.Model.EventArgs;

namespace WinFromMediaPlayerDemo.Common
{

    /// <summary>
    /// 文件多区块下载
    /// </summary>
    public class MulitPartionDownload
    {

        #region 自定义事件
        /// <summary>
        /// 文件下载进度改变事件
        /// </summary>
        public event EventHandler<FileDownPercentageChangedEventArgs> FileDownPercentageChangedEventHandler;
        /// <summary>
        /// 同步临时文件后事件
        /// </summary>
        public event EventHandler<SynchronizedTempFileEventArgs> SynchronizedTempFileEventHandler;

        #endregion
        /// <summary>
        /// 文件队列
        /// </summary>
        public ConcurrentQueue<RemoteFileInfo> Files { get; private set; }
        private IMessageProvider _MessageProvider;

        public string BaseDownloadUrl { get; set; }

        public ulong DefaultPartionSize { get; set; }
        /// <summary>
        /// 所有文件总大小
        /// </summary>
        public ulong TotalFileSize { get; private set; } = 0;
        /// <summary>
        /// 总体已下载文件大小
        /// </summary>
        public ulong TotalDownloadFileSize { get; private set; } = 0;

        public ulong CurrentFileDownloadSize { get; private set; } = 0;


        public MulitPartionDownload(IMessageProvider messageProvider, ConcurrentQueue<RemoteFileInfo>  downloadFiles,string baseDownloadUrl)
        {
            this._MessageProvider = messageProvider;
            this.Files = downloadFiles;
            this.BaseDownloadUrl = baseDownloadUrl;
            //默认分区大小，配置文件中存储单位是kb，这里转换一下
            this.DefaultPartionSize = Global.Default.PartionSize*1024;


            if (Files == null || Files.IsEmpty)
            {
                _MessageProvider.Show("文件队列为空");
                return;
            }
            foreach (var item in Files)
            {
                this.TotalFileSize += item.FileSize;
            }
        }

        public void Run()
        {
            if(Files.IsEmpty)
            {
                return;
            }
            RemoteFileInfo currentFile;
            bool result = Files.TryDequeue(out currentFile);
            if(!result)
            {
                _MessageProvider.Show("文件队列异常");
                return;
            }
            DownloadPerFile(currentFile);
        }

        /// <summary>
        /// 根据文件大小，智能计算文件分区大小
        /// </summary>
        /// <param name="fileSize"></param>
        /// <param name="partionSize">指定的分区大小</param>
        /// <returns></returns>
        private uint ComputePartionCount(ulong fileSize, ulong partionSize)
        {
            uint partionCount = 1;
            if (fileSize % partionSize == 0)//整除
            {
                partionCount = (uint)(fileSize / partionSize);
            }
            partionCount = (uint)(fileSize / partionSize + 1);
            return partionCount;
        }
        /// <summary>
        /// 下载当前文件的每一块分区数据
        /// </summary>
        /// <param name="currentFileInfo"></param>
        /// <param name="partionCount"></param>
        private void DownloadEachPartionData(RemoteFileInfo currentFileInfo, uint partionCount)
        {
            currentFileInfo.TotalPartionCount = partionCount;
            ulong currentFileSize = currentFileInfo.FileSize;
            ulong startIndex = 0;
            ulong requestFileSize = 0;

            //缓存文件目录
            string partionCacheDirecotry = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments), currentFileInfo.FileName);
            //如果缓存目录存在，需要进行“断点”检查
            bool isCheckLocalCache = Directory.Exists(partionCacheDirecotry);
            for (ulong i = 0; i < partionCount; i++)
            {
                using (WebClient client = new WebClient())
                {
                    
                    
                    requestFileSize = DefaultPartionSize;
                    startIndex = i * DefaultPartionSize;
                    if(requestFileSize +startIndex > currentFileSize)
                    {
                        requestFileSize = currentFileSize - startIndex;
                    }
                    string currentTmpFile = Path.Combine(partionCacheDirecotry, $"{i}.tmp");
                    //缓存文件存在
                    if (isCheckLocalCache && File.Exists(currentTmpFile) && (ulong)new FileInfo(currentTmpFile).Length == requestFileSize)
                    {
                        //同步下载字节数
                        this.TotalDownloadFileSize += requestFileSize;
                        this.CurrentFileDownloadSize += requestFileSize;
                        //计算进度
                        byte totalPercentage = (byte)Math.Floor(this.TotalDownloadFileSize / (decimal)this.TotalFileSize * 100);
                        byte currentPercentage = (byte)Math.Floor(this.CurrentFileDownloadSize / (decimal)currentFileInfo.FileSize * 100);
                        SynchronizedTempFileEventHandler?.Invoke(this, new SynchronizedTempFileEventArgs()
                        {
                            CurrentFilePercentage = currentPercentage,
                            TotalFilesPercentage = totalPercentage
                        });
                        continue;
                    }


                    Uri downUri = new Uri($"{BaseDownloadUrl}?fileName={currentFileInfo.FileName}&startIndex={startIndex}&size={requestFileSize}");
                    

                    client.DownloadDataCompleted += Client_DownloadDataCompleted;

                    client.DownloadDataAsync(downUri, new PartionFileInfo() {
                        FileInfo = currentFileInfo,
                        PartionIndex = i,
                        PartionSize = requestFileSize,
                        PartionStatu = -1
                    });

                }
            }
        }
        private string formatBytesData(byte[]data,int limit)
        {
            StringBuilder sb = new StringBuilder();
            byte[] limitData = data.Take(limit).ToArray();
            foreach (var item in limitData)
            {
                sb.Append(item.ToString("X2"));
            }
            return sb.ToString();
        }

        private void Client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            PartionFileInfo partionFileInfo = e.UserState as PartionFileInfo;
            if(partionFileInfo == null)
            {
                return;
            }

            this.TotalDownloadFileSize += (ulong)e.Result.Length;
            this.CurrentFileDownloadSize += (ulong)e.Result.Length;

            //计算进度
            byte totalPercentage = (byte)Math.Floor(this.TotalDownloadFileSize / (decimal)this.TotalFileSize*100);
            byte currentPercentage = (byte)Math.Floor(this.CurrentFileDownloadSize / (decimal)partionFileInfo.FileInfo.FileSize*100);


            FileDownPercentageChangedEventHandler?.Invoke(this, new FileDownPercentageChangedEventArgs()
            {
                PartionFileInfo = partionFileInfo,
                CurrentFilePercentage = currentPercentage,
                TotalFilesPercentage = totalPercentage,
                Data = e.Result,
                PartionIndex = (uint)partionFileInfo.PartionIndex,
            });

            //当前文件下载完毕
            if(currentPercentage == 100)
            {
                this.CurrentFileDownloadSize = 0;
                Run();//执行下一个文件
            }
        }

        private void DownloadPerFile(RemoteFileInfo fileInfo)
        {
            if(string.IsNullOrEmpty(this.BaseDownloadUrl))
            {
                _MessageProvider.Show("文件下载地址无效");
                return;
            }
            try
            {
                uint partionCount = ComputePartionCount(fileInfo.FileSize, this.DefaultPartionSize);
                DownloadEachPartionData(fileInfo, partionCount);
            }
            catch (WebException)
            {
                _MessageProvider.Show("网络异常，连接服务器失败");
            }
            catch(Exception e)
            {
                _MessageProvider.Show("系统异常：" + e.Message);
            }

            
        }
    }
}
